home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / MCASM.RAR / MC_ASM.EXE / WROX_ASM / CH2 / TEST_CPU.ASM
Assembly Source File  |  1994-12-12  |  23KB  |  477 lines

  1. TITLE TEST_CPU - Determine Intel Microprocessor Type
  2. ;
  3. ; MODULE NAME:     TEST_CPU.ASM
  4. ; AUTHOR:          Dan Wronski
  5. ; DATE:            12/02/94
  6. ; VERSION:         1.0
  7. ; LANGUAGE:        Turbo/MASM Assembly
  8. ; DESCRIPTION:     Determine Intel microprocessor type while running in
  9. ;                  8086 Real Address Mode or Virtual 8086 Mode.
  10. ; COPYRIGHT:       (C) Dan Wronski 1994
  11. ; CHANGE HISTORY:  See below
  12. ; 12/02/94 - DMW - Created
  13. ;
  14.                .MODEL small
  15.                .STACK 100h
  16.                .8086
  17. FLAG_ID_MASK   equ 200000h
  18. FLAG_AC_MASK   equ 40000h
  19. FLAG_NT_MASK   equ 4000h
  20. CPU_ID         MACRO
  21.                db      0Fh                     ; New Pentium CPUID instruction
  22.                db      0A2h
  23.                ENDM
  24.  
  25. Convert_Hex    MACRO
  26.                LOCAL   Skip
  27.                and     al,0Fh                  ; Only want low nibble
  28.                or      al,30h                  ; Assume 0-9
  29.                cmp     al,39H                  ; Is it?
  30.                jbe     short Skip              ; Yes
  31.                add     al,7                    ; No, adjust for A-F
  32. Skip:
  33.                ENDM
  34.                .DATA
  35. FPU_Status     dw      0
  36. Text_Title     db      13,10,'        TEST_CPU V1.0'
  37.                db      13,10,'   Get Microprocessor Type'
  38.                db      13,10,'Copyright (c) Dan Wronski 1994',13,10,13,10,'$'
  39. Text_Unknown   db      'Could not recognize the microprocessor in this system.',13,10,'$'
  40. Text_FPU       db      'This system also has a numeric coprocessor.',13,10,'$'
  41. Text_CPU_Start db      'This system has an Intel ',0
  42. Text_8088      db      '8088',0
  43. Text_8086      db      '8086',0
  44. Text_80188     db      '80188',0
  45. Text_80186     db      '80186',0
  46. Text_80286     db      '80286',0
  47. Text_386DX     db      '386DX',0
  48. Text_386SX     db      '386SX',0
  49. Text_486DX     db      '486DX',0
  50. Text_486SX     db      '486SX',0
  51. Text_Old_End   db      ' or compatible microprocessor.',13,10,'$',0
  52. Text_Intel     db      'genuine ',0
  53. Text_Clone     db      'compatible ',0
  54. Text_New_Type  db      'microprocessor:',13,10,'  family='
  55. Text_Family    db      'xh, model='
  56. Text_Model     db      'xh, stepping='
  57. Text_Stepping  db      'xh'
  58. Text_Pentium   db      ' (Pentium)',0
  59. Text_New_End   db      '.',13,10,'$',0
  60. Text_Buffer    db      250 dup (?)             ; Max of three 80-byte lines
  61.                .CODE
  62. Main           proc
  63.                mov     ax,@data
  64.                mov     ds,ax                   ; Set DS to our data segment
  65.                mov     es,ax                   ; And ES also
  66.                cld                             ; Clear string direction
  67.  
  68.                lea     dx,Text_Title
  69.                mov     ah,9                    ; DOS print string function #
  70.                int     21h
  71. ;
  72. ; Check if the microprocessor is one of the "old" ones prior to Pentium
  73. ;
  74.                lea     di,Text_Buffer          ; Set up initial CPU string
  75.                lea     si,Text_CPU_Start
  76.                call    Copy_String
  77.  
  78.                call    Check_8086
  79.                jnz     Print_Old               ; Print if 8088/8086/80188/80186
  80.  
  81.                call    Check_80286
  82.                jnz     Print_Old               ; Print if 80286
  83.  
  84.                call    Check_80386
  85.                jnz     Print_Old               ; Print if 386DX or 386SX
  86.  
  87.                call    Check_80486
  88.                jnz     Print_Old               ; Print if 486DX or 486SX
  89. ;
  90. ; Now we have a "new" microprocessor that supports the CPUID instruction
  91. ;
  92.                call    Check_Pentium
  93.                jnz     Print_New               ; Print if we know what it is
  94.  
  95.                lea     dx,Text_Unknown         ; No match so tell user
  96.                mov     ah,9                    ; DOS print string function #
  97.                int     21h
  98.                jmp     Print_FPU
  99. Print_New:
  100.                call    Copy_String             ; Copy genuine/compatible text
  101.                lea     si,Text_New_Type        ; Copy cpu type to buffer
  102.                call    Copy_String
  103.                lea     si,Text_New_End         ; Add ending to text string
  104.                call    Copy_String
  105.                jmp     Print_CPU
  106. Print_Old:
  107.                call    Copy_String             ; Copy cpu type to buffer
  108.                lea     si,Text_Old_End         ; Add ending to text string
  109.                call    Copy_String
  110. Print_CPU:
  111.                lea     dx,Text_Buffer
  112.                mov     ah,9                    ; DOS print string function #
  113.                int     21h
  114. Print_FPU:
  115.                call    Check_Coprocessor       ; Coprocessor in system?
  116.                jnz     Done                    ; No,  we are finish
  117.                lea     dx,Text_FPU             ; Yes, tell user
  118.                mov     ah,9                    ; DOS print string function #
  119.                int     21h
  120. Done:
  121.                mov     ax,4c00h                ; DOS terminate function #
  122.                int     21h
  123. Main           endp
  124.  
  125. ;-----------------------------------------------------------------------------
  126. ; Copy_String - Copy one source string to a target buffer. This routine
  127. ;               requires the string direction flag updated via CLD, and
  128. ;               the end-of-string character in the source string to be zero.
  129. ;               Also this routine does no limit checking on string length.
  130. ;   Inputs
  131. ;     SI = Source string address
  132. ;     DI = Target buffer address
  133. ;
  134. ;   Outputs
  135. ;     AX = Undefined
  136. ;     SI = Undefined
  137. ;     DI = Address of end-of-string character
  138. ;-----------------------------------------------------------------------------
  139. Copy_String    proc
  140.                lodsb                           ; Get next source character
  141.                stosb                           ; Store in target buffer
  142.                or      al,al                   ; Reached end of string?
  143.                jnz     Copy_String             ; No,  get next character
  144.                dec     di                      ; Yes, point to end of string
  145.                ret
  146. Copy_String    endp
  147.  
  148. ;-----------------------------------------------------------------------------
  149. ; Check_Prefetch  - Check to see if we can modify an instruction at a specific
  150. ;                   displacement after a jump. We can not modify displacement 0
  151. ;                   and we assume we can always modify after displacement 64.
  152. ;   Inputs
  153. ;     AX = instruction displacement (limited to 0 - 64)
  154. ;
  155. ;   Outputs
  156. ;     AX = 0 if could not modify instruction else undefined (ZF matches results)
  157. ;-----------------------------------------------------------------------------
  158. Check_Prefetch proc
  159.                push    es                      ; Save registers
  160.                push    si
  161.                push    di
  162.                push    dx
  163.                push    cx
  164.                push    bx
  165.  
  166.                mov     dx,ax                   ; Save displacement
  167.                or      ax,ax                   ; Zero displacement?
  168.                jz      Finish_Test             ; Yes, no need to test
  169.                cmp     ax,64                   ; Displacement too big?
  170.                ja      Finish_Test             ; Yes, no need to test
  171. ;
  172. ; We need to align the jump target address on a 16-byte boundary
  173. ;
  174.                mov     ax,cs
  175.                mov     es,ax                   ; Load ES with code segment
  176.                lea     bx,cs:Test_Code         ; Get initial target address
  177.                mov     di,bx
  178.                mov     cx,80
  179.                mov     al,90h                  ; Keep code reusable by filling
  180.                rep     stosb                   ;   target area with nop's
  181.                add     bx,15                   ;   Also ensure 4K page is loaded
  182.                and     bx,0FFF0h               ; Align target to 16-byte
  183.                mov     di,bx
  184.                mov     al,0AAh                 ; Get "stosb" instruction
  185.                stosb                           ; Place it at 0 displacement
  186.                dec     di                      ; Restore 0 displacement offset
  187.                add     di,dx                   ; Point to desired displacement
  188.                mov     al,40h                  ; Get "inc ax" op-code to store
  189.                cli                             ; Disable interrupts
  190.                jmp     bx                      ; Flush prefetch queue
  191. Test_Code:
  192.                db      80 dup (90h)            ; string of "nop" instructions
  193.                sti                             ; Restore interrupts
  194.                cmp     al,40h                  ; Modified instruction?
  195.                jne     Finish_Test             ; Yes
  196.                xor     ax,ax                   ; No
  197. Finish_Test:
  198.                pop     bx                      ; Restore registers
  199.                pop     cx
  200.                pop     dx
  201.                pop     di
  202.                pop     si
  203.                pop     es
  204.                or      ax,ax                   ; Set SF to match results
  205.                ret
  206. Check_Prefetch endp
  207.  
  208. ;-----------------------------------------------------------------------------
  209. ; Check_Coprocessor - Determine if a numeric coprocessor is in the system
  210. ;                     by trying to retrieve the hardware status.
  211. ;   Inputs
  212. ;     None
  213. ;
  214. ;   Outputs
  215. ;     AX = 0 if coprocessor exists else undefined (ZF matches results)
  216. ;-----------------------------------------------------------------------------
  217. Check_Coprocessor proc
  218.                mov     FPU_Status,0FFFFh       ; Reset floating point status
  219.                fninit                          ; Reset floating point hardware
  220.                fnstsw  FPU_Status              ; Get floating point h/w status
  221.                mov     ax,FPU_Status           ; FPU_Status = 0 if h/w exists
  222.                or      ax,ax                   ; Set ZF to match results
  223.                ret
  224. Check_Coprocessor endp
  225.  
  226. ;-----------------------------------------------------------------------------
  227. ; Check_XXXXX - Determine if the CPU type is a specific microprocessor.
  228. ;               If a match is found then load SI with the CPU text string,
  229. ;               otherwise load zero into SI. ZF matches SI contents.
  230. ;   Inputs
  231. ;     None
  232. ;
  233. ;   Outputs
  234. ;     AX = Undefined
  235. ;     SI = Address of CPU text string or zero (ZF matches results)
  236. ;-----------------------------------------------------------------------------
  237. Check_8086     proc
  238.                push    di                      ; Save registers
  239.                push    cx
  240.                xor     si,si                   ; Assume no match
  241. ;
  242. ; First test is whether we can reset the NT flag
  243. ;
  244.                pushf                           ; Save original flags
  245.                cli                             ; Disable interrupts
  246.                pushf                           ; Get current flags
  247.                pop     ax
  248.                or      ax,FLAG_NT_MASK         ; Clear only NT flag
  249.                xor     ax,FLAG_NT_MASK
  250.                push    ax
  251.                popf                            ; Update flags
  252.                pushf
  253.                pop     ax                      ; Get "updated" flags
  254.                popf                            ; Restore original flags
  255.                                                ; (and restore interrupts)
  256.                test    ax,FLAG_NT_MASK         ; Changed NT flag?
  257.                jz      Finish_8086             ; Yes, not 8088/8086/80188/80186
  258.                lea     si,Text_8088            ; No, assume we have an 8088
  259.                lea     di,Text_8086            ; Second choice would be 8086
  260. ;
  261. ; Test whether it's an 8088/8086 or 80188/80186. The later will mask shift
  262. ; counts with 1Fh before shifting.
  263. ;
  264.                mov     cl,21h                  ; Can't use 20h or ZF may
  265.                mov     al,1                    ; not get set correctly
  266.                shl     al,cl                   ; Is it an 8088/8086?
  267.                jz      Check_8_Bit_Bus         ; Yes
  268.                lea     si,Text_80188           ; No, assume we have an 80188
  269.                lea     di,Text_80186           ; Second choice would be 80186
  270. ;
  271. ; Now we need to determine whether it's 8088/80188 (8-bit data bus) or
  272. ; 8086/80186 (16-bit data bus). Test whether the microprocessor has the
  273. ; 8-bit or 16-bit prefetcher unit by modifying an instruction after a jump.
  274. ;
  275. Check_8_Bit_Bus:
  276.                mov     ax,3                    ; Modify displacment 3
  277.                call    Check_Prefetch          ; Did we modify instruction?
  278.                jnz     Finish_8086             ; Yes, use first choice (8-bit)
  279.                mov     si,di                   ; No,  use second choice (16-bit)
  280. Finish_8086:
  281.                pop     cx                      ; Restore registers
  282.                pop     di
  283.                or      si,si                   ; Set ZF to match results
  284.                ret
  285. Check_8086     endp
  286.  
  287. Check_80286    proc
  288.                xor     si,si                   ; Assume no match
  289. ;
  290. ; Test whether we can set the NT flag (always zero in Real Mode on 80286)
  291. ;
  292.                pushf                           ; Save original flags
  293.                cli                             ; Disable interrupts
  294.                pushf                           ; Get current flags
  295.                pop     ax
  296.                or      ax,FLAG_NT_MASK         ; Set NT flag
  297.                push    ax
  298.                popf                            ; Update flags
  299.                pushf
  300.                pop     ax                      ; Get "updated" flags
  301.                popf                            ; Restore original flags
  302.                                                ; (and restore interrupts)
  303.                test    ax,FLAG_NT_MASK         ; Changed NT flag?
  304.                jnz     Finish_80286            ; Yes, not 80286
  305.                lea     si,Text_80286           ; No,  must be 80286
  306. Finish_80286:
  307.                or      si,si                   ; Set ZF to match results
  308.                ret
  309. Check_80286    endp
  310.  
  311. ;**********************************************************************
  312. ; From here to the end of this module is mixed 16-bit and 32-bit code
  313. ;**********************************************************************
  314.  
  315.                .386
  316. Check_80386    proc
  317.                xor     si,si                   ; Assume no match
  318. ;
  319. ; Test whether we can set the AC flag (always zero on 80386), but we need
  320. ; to make sure the stack is properly aligned to avoid an exception on
  321. ; 80486 or higher microprocessor.
  322. ;
  323.                push    bp                      ; Save register
  324.                mov     bp,sp                   ; Save current stack pointer
  325.                and     sp,0FFFCh               ; Align stack to 32-bit
  326.                push    eax                     ; Save 32-bit register
  327.  
  328.                pushfd                          ; Save original 32-bit flags
  329.                cli                             ; Disable interrupts
  330.                pushfd                          ; Get current flags
  331.                pop     eax
  332.                or      eax,FLAG_AC_MASK        ; Set AC flag
  333.                push    eax
  334.                popfd                           ; Update flags
  335.                pushfd
  336.                pop     eax                     ; Get "updated" flags
  337.                popfd                           ; Restore original 32-bit flags
  338.                                                ; (and restore interrupts)
  339.                test    eax,FLAG_AC_MASK        ; AC flag still set?
  340.                jnz     short Finish_80386      ; Yes, not an 80386
  341. ;
  342. ; Now we need to determine whether it's 386SX (16-bit data bus) or
  343. ; 386DX (32-bit data bus). It is possible the system could have a
  344. ; 16-bit data bus and still have an 386DX (it supports a 16-bit data bus)
  345. ; but it's very unlikely because the system performance would be slightly
  346. ; worse than having a 386SX. So test whether the microprocessor has the
  347. ; 16-bit or 32-bit prefetcher unit by modifying an instruction after a jump.
  348. ;
  349.                lea     si,Text_386DX           ; Assume 386DX
  350.                mov     ax,11                   ; Modify displacment 11
  351.                call    Check_Prefetch          ; Did we modify instruction?
  352.                jz      short Finish_80386      ; No,  must have 32-bit bus
  353.                lea     si,Text_386SX           ; Yes, has to be 386SX
  354.                                                ; (or might as well be)
  355. Finish_80386:
  356.                pop     eax                     ; Restore 32-bit register
  357.                mov     sp,bp                   ; Restore stack pointer
  358.                pop     bp                      ; Restore register
  359.                or      si,si                   ; Set ZF to match results
  360.                ret
  361. Check_80386    endp
  362.  
  363. Check_80486    proc
  364.                xor     si,si                   ; Assume no match
  365. ;
  366. ; Make sure the stack is aligned to a 32-bit boundary
  367. ;
  368.                push    bp                      ; Save register
  369.                mov     bp,sp                   ; Save current stack pointer
  370.                and     sp,0FFFCh               ; Align stack to 32-bit
  371.                push    ebx                     ; Save 32-bit registers
  372.                push    eax
  373. ;
  374. ; Test whether the CPUID instruction is supported on this microprocessor by
  375. ; toggling the ID flag.
  376. ;
  377.                pushfd                          ; Save original 32-bit flags
  378.                cli                             ; Disable interrupts
  379.                pushfd                          ; Get current flags
  380.                pop     eax
  381.                mov     ebx,eax
  382.                and     ebx,FLAG_ID_MASK        ; Save ID flag
  383.                xor     eax,FLAG_ID_MASK        ; Toggle ID flag
  384.                push    eax
  385.                popfd                           ; Update flags
  386.                pushfd
  387.                pop     eax                     ; Get "updated" flags
  388.                popfd                           ; Restore original 32-bit flags
  389.                                                ; (and restore interrupts)
  390.                and     eax,FLAG_ID_MASK        ; Only want ID flag
  391.                cmp     eax,ebx                 ; ID flag changed?
  392.                jne     short Finish_80486      ; Yes, not an 80486
  393. ;
  394. ; The only difference between a 486DX and 486SX is whether the numeric
  395. ; coprocessor unit is present inside the chip. (The 486SX doesn't have one.)
  396. ; So if there is no coprocessor present it has to be a 486SX. But if one
  397. ; is present then it could be a 486SX with 487SX or a 486DX. Since installing
  398. ; a 487SX disables the 486SX, for all practical purposes a 487SX is a
  399. ; 486DX and will therefore be reported as a 486DX.
  400. ;
  401.                lea     si,Text_486SX           ; Assume 486SX
  402.                call    Check_Coprocessor       ; Coprocessor in system?
  403.                jnz     short Finish_80486      ; No,  has to be 486SX
  404.                lea     si,Text_486DX           ; Yes, treat as a 486DX
  405. Finish_80486:
  406.                pop     eax                     ; Restore 32-bit registers
  407.                pop     ebx
  408.                mov     sp,bp                   ; Restore stack pointer
  409.                pop     bp                      ; Restore register
  410.                or      si,si                   ; Set ZF to match results
  411.                ret
  412. Check_80486    endp
  413.  
  414. Check_Pentium  proc
  415.                xor     si,si                   ; Assume no match
  416. ;
  417. ; Make sure the stack is aligned to a 32-bit boundary
  418. ;
  419.                push    bp                      ; Save register
  420.                mov     bp,sp                   ; Save current stack pointer
  421.                and     sp,0FFFCh               ; Align stack to 32-bit
  422.                push    edx                     ; Save 32-bit registers
  423.                push    ecx
  424.                push    ebx
  425.                push    eax
  426. ;
  427. ; First determine whether we can get the family, model, and stepping level ID
  428. ;
  429.                xor    eax,eax                  ; Get ID string
  430.                CPU_ID
  431.                cmp    eax,1                    ; Can we get the ID?
  432.                jb     short Finish_Pentium     ; No, treat as unknown
  433. ;
  434. ; Check whether this is a "genuine" Intel microprocessor.
  435. ;
  436.                lea    si,Text_Clone            ; Assume not genuine Intel
  437.                cmp    ebx,756E6547h            ; 1st part match Intel ID?
  438.                jne    short Check_Family       ; No
  439.                cmp    edx,49656E69h            ; 2nd part match Intel ID?
  440.                jne    short Check_Family       ; No
  441.                cmp    ecx,6C65746Eh            ; 3rd part match Intel ID?
  442.                jne    short Check_Family       ; No
  443.                lea    si,Text_Intel            ; Yes, have the genuine part
  444. ;
  445. ; Get microprocessor family, model, and stepping level ID
  446. ;
  447. Check_Family:
  448.                mov     eax,1                   ; Get family, model, stepping ID
  449.                CPU_ID
  450.                mov     bx,ax                   ; Save family, model, stepping ID
  451.                Convert_Hex                     ; Make stepping ID printable
  452.                mov     Text_Stepping,al        ; Place it in text
  453.                mov     al,bl
  454.                shr     al,4                    ; Get model ID
  455.                Convert_Hex                     ; Make it printable
  456.                mov     Text_Model,al           ; Place it in text
  457.                mov     al,bh                   ; Get family ID
  458.                Convert_Hex                     ; Make it printable
  459.                mov     Text_Family,al          ; Place it in text
  460.  
  461.                mov     Text_Pentium,' '        ; Make code reuseable
  462.                cmp     al,'5'                  ; Is it a Pentium?
  463.                je      short Finish_Pentium    ; Yes, we are finish
  464.                mov     Text_Pentium,0          ; No,  leave out Pentium text
  465. Finish_Pentium:
  466.                pop     eax                     ; Restore 32-bit registers
  467.                pop     ebx
  468.                pop     ecx
  469.                pop     edx
  470.                mov     sp,bp                   ; Restore stack pointer
  471.                pop     bp                      ; Restore register
  472.                or      si,si                   ; Set ZF to match results
  473.                ret
  474. Check_Pentium  endp
  475.  
  476.                end Main
  477.